package com.galen.manual.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 雪花算法ID生成器
 *
 * 雪花算法生成64位的long类型ID：
 * - 1位符号位（0）
 * - 41位时间戳（毫秒级，可用69年）
 * - 10位机器ID（5位数据中心ID + 5位机器ID）
 * - 12位序列号（每毫秒内可生成4096个ID）
 */
@Slf4j
@Component
public class SnowflakeIdGenerator {

    /**
     * 起始时间戳（2023-01-01 00:00:00）
     */
    private static final long EPOCH = 1672531200000L;

    /**
     * 机器ID所占的位数
     */
    private static final long MACHINE_ID_BITS = 5L;

    /**
     * 数据中心ID所占的位数
     */
    private static final long DATACENTER_ID_BITS = 5L;

    /**
     * 序列号所占的位数
     */
    private static final long SEQUENCE_BITS = 12L;

    /**
     * 机器ID向左移的位数
     */
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 数据中心ID向左移的位数
     */
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;

    /**
     * 时间戳向左移的位数
     */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATACENTER_ID_BITS;

    /**
     * 机器ID的最大值
     */
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);

    /**
     * 数据中心ID的最大值
     */
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);

    /**
     * 序列号的最大值
     */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * 数据中心ID（默认为1）
     */
    private final long datacenterId;

    /**
     * 机器ID（默认为1）
     */
    private final long machineId;

    /**
     * 序列号
     */
    private long sequence = 0L;

    /**
     * 上一次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     */
    public SnowflakeIdGenerator() {
        this(1L, 1L);
    }

    /**
     * 构造函数
     * @param datacenterId 数据中心ID
     * @param machineId 机器ID
     */
    public SnowflakeIdGenerator(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("数据中心ID必须在0-" + MAX_DATACENTER_ID + "之间");
        }
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException("机器ID必须在0-" + MAX_MACHINE_ID + "之间");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
        log.info("雪花算法ID生成器初始化完成，数据中心ID: {}, 机器ID: {}", datacenterId, machineId);
    }

    /**
     * 生成下一个ID
     * @return 生成的ID
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        // 处理时钟回拨
        if (timestamp < lastTimestamp) {
            long offset = lastTimestamp - timestamp;
            if (offset <= 5) {
                try {
                    // 等待时钟追上
                    Thread.sleep(offset << 1);
                    timestamp = System.currentTimeMillis();
                    if (timestamp < lastTimestamp) {
                        throw new RuntimeException("时钟回拨时间过长，无法生成ID");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("时钟回拨等待被中断", e);
                }
            } else {
                throw new RuntimeException("时钟回拨时间过长，无法生成ID");
            }
        }

        // 同一毫秒内
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 序列号溢出，等待下一毫秒
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 不同毫秒，序列号重置为0
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        // 生成ID
        return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }

    /**
     * 等待下一毫秒
     * @param lastTimestamp 上一次时间戳
     * @return 下一毫秒时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 生成ID字符串
     * @return ID字符串
     */
    public String nextIdString() {
        return String.valueOf(nextId());
    }

    /**
     * 解析ID获取时间戳
     * @param id ID
     * @return 时间戳
     */
    public long getTimestamp(long id) {
        return (id >> TIMESTAMP_SHIFT) + EPOCH;
    }

    /**
     * 解析ID获取数据中心ID
     * @param id ID
     * @return 数据中心ID
     */
    public long getDatacenterId(long id) {
        return (id >> DATACENTER_ID_SHIFT) & MAX_DATACENTER_ID;
    }

    /**
     * 解析ID获取机器ID
     * @param id ID
     * @return 机器ID
     */
    public long getMachineId(long id) {
        return (id >> MACHINE_ID_SHIFT) & MAX_MACHINE_ID;
    }

    /**
     * 解析ID获取序列号
     * @param id ID
     * @return 序列号
     */
    public long getSequence(long id) {
        return id & MAX_SEQUENCE;
    }
}